//
// Created by lz on 3/14/21.
//

#include "net/TcpClient.h"

#include "base/Logging.h"
#include "net/Connector.h"
#include "net/EventLoop.h"
#include "net/SocketsOps.h"

#include <cstdio>

using namespace lzweb;
using namespace lzweb::net;

namespace lzweb
{
	namespace net
	{
		namespace detail
		{
			void removeConnection(EventLoop* loop, const TcpConnectionPtr& conn)
			{
				loop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
			}

			//留空？
			void removeConnector(const ConnectorPtr& connector)
			{
				// connector->
			}


		} // namespace detail
	}  //namespace net
}  // namespace lzweb

TcpClient::TcpClient(EventLoop* loop,
	const InetAddress& serverAddr,
	const string& nameArg)
	: loop_(CHECK_NOTNULL(loop)),
	  name_(nameArg),
	  connectionCallback_(defaultConnectionCallback),
	  messageCallback_(defaultMessageCallback),
	  retry_(false),
	  connect_(true),
	  nextConnId_(1)
{
	connector_->setNewConnectionCallback(
		std::bind(&TcpClient::newConnection, this, _1));
	//FIXME setConnectFailedCallback
	LOG_INFO << " TcpClient::TcoClient[" << name_
			 << "] - connector " << get_pointer(connector_);
}

TcpClient::~TcpClient()
{
	LOG_INFO << " TcpClient::~TcpClient[" << name_
			 << "] - connector " << get_pointer(connector_);
	TcpConnectionPtr conn;
	bool unique = false;
	{
		MutexLockGuard lock(mutex_);
		unique = connection_.unique();
		conn = connection_;
	}
	if(conn)
	{
		assert(loop_ == conn->getLoop());
		//FIXME: not 100% safe, if we are in different thread
		//不安全是因为可能loop_繁忙，导致conn的forceClose中的closeCallback 函数没有设置？
		CloseCallback cb = std::bind(&detail::removeConnection, loop_, _1);
		loop_->runInLoop(std::bind(&TcpConnection::setCloseCallback, conn, cb));
		if(unique)
		{
			conn->forceClose();
		}

	}
	else
	{
		connector_->stop();
		//FIXME : HACK
		loop_->runAfter(1, std::bind(&detail::removeConnector, connector_));
	}
}

//核心就是Connector 中的start() 函数
void TcpClient::connect()
{
	//FIXME: check state
	LOG_INFO << "TcpClient::connect[" << name_ << "] - connecting to "
			 << connector_->serverAddress().toIpPort();
	connect_ = true;
	connector_->start();
}

//disconnect 用的TcpConnection 的shutdown
void TcpClient::disconnect()
{
	connect_ = false;
	{
		MutexLockGuard lock(mutex_);
		if(connection_)
		{
			connection_->shutdown();
		}
	}
}

// stop()用的Connector的stop()
void TcpClient::stop()
{
	connect_ = false;
	connector_->stop();
}

//当该函数接收到sockfd的时候，系统已经在该sockfd中建立了tcp连接，该函数用于创建维护该tcp连接所需要的东西
void TcpClient::newConnection(int sockfd)
{
	loop_->assertInLoopThread();
	InetAddress peerAddr(sockets::getPeerAddr(sockfd));
	char buf[32];
	snprintf(buf, sizeof buf, ":%s#%d", peerAddr.toIpPort().c_str(), nextConnId_);
	++nextConnId_;
	string connName = name_+ buf;
	InetAddress localAddr(sockets::getLocalAddr(sockfd));
	//FIXME poll with zero timeout to double confirm the new connection
	//FIXME use make_shared if necessary
	TcpConnectionPtr conn = std::make_shared<TcpConnection>(
			loop_,
			connName,
			sockfd,
			localAddr,
			peerAddr);
	conn->setConnectionCallback(connectionCallback_);
	conn->setMessageCallback(messageCallback_);
	conn->setWriteCompleteCallback(writeCompleteCallback_);
	conn->setCloseCallback(
		std::bind(&TcpClient::removeConnection, this, _1)
	); //FIXME : unsafe

	{
		MutexLockGuard lock(mutex_);
		connection_ = conn;
	}
	conn->connectEstablished();
}

void TcpClient::removeConnection(const TcpConnectionPtr& conn)
{
	loop_->assertInLoopThread();
	assert(loop_ == conn->getLoop());

	{
		MutexLockGuard lock(mutex_);
		assert(connection_ == conn);
		connection_.reset();
	}

	loop_->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
	if(retry_ && connect_)
	{
		LOG_INFO << "TcpClient::connect[" << name_ << "] - Reconnecting to "
				 << connector_->serverAddress().toIpPort();
		connector_->restart();
	}
}
